from concepts.attributes import Attribute, Object, _


class Effect(Object):
    name =_ (None)
    
    duration = Attribute(writable = True, track_changes = True)
    target   = Attribute(writable = True)
    
    @property
    def copy(self):
        return self.__class__(self.duration)
    
    def day(self, day, month, year):
        pass
    
    def starts(self):
        pass
    
    def ceases(self):
        pass
    
    def start(self):
        self.starts()
        self.changed()
    
    def cease(self):
        self._duration = 0
        
        self.ceases()
        self.changed()
    
    def on_changed(self):
        if self.target is not None:
            self.target.changed()
    
    def apply(self, day, month, year):
        if self.duration <= 0:
            return
        
        self.day(day, month, year)
        self.duration -= 1
        
        if self.duration == 0:
            self.cease()
    
    def __repr__(self):
        return '<%s for %dd>' % (self.name, self.duration)


class Effects(Object):
    target  = Attribute()
    effects = Attribute(initial = {}, convert = dict, transform = dict)
    
    def all(self, cls):
        for effect in self.effects.keys():
            if issubclass(effect, cls):
                yield self.effect(effect)
    
    def has(self, cls):
        return self.has_effect(cls)
    
    def has_effect(self, cls):
        return cls in self._effects
    
    def effect(self, cls):
        return self._effects.get(cls, None)
    
    def add_effect(self, effect):
        if self.has_effect(effect.__class__):
            self.effect(effect.__class__).duration += effect.duration
        else:
            self.set_effect(effect)
    
    def set_effect(self, effect):
        effect = effect.copy
        
        self._effects[effect.__class__] = effect
        
        effect.target = self.target
        effect.start()
    
    def remove_effect(self, cls):
        effect = self.effect(cls)
        effect.cease()
        
        del self_effects[cls]
    
    def day(self, day, month, year):
        for effect in self.effects.values():
            effect.apply(day, month, year)
            
            if effect.duration <= 0:
                del self._effects[effect.__class__]
    
    def __repr__(self):
        return '<[%s]>' % ', '.join(sorted(repr(x) for x in self.effects.values()))
